﻿using System.Collections.Concurrent;

namespace Codemonk.Pease.Server
{
    public class Scheduler : IScheduler
    {
        private Scheduler() { }
        /// <summary>
        /// 工作数
        /// </summary>
        private const int WORKERCOUNT = 4;
        private static object _sync = new object();
        private static Scheduler instance;
        private static long mIndex = 0;
        public static Scheduler Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (_sync)
                    {
                        if (instance == null)
                        {
                            instance = new Scheduler();
                            instance.Initialize();
                        }
                    }
                }
                return instance;
            }
        }

        private void Initialize()
        {
            workers = new InnerWorker[WORKERCOUNT];
            for (int i = 0; i < WORKERCOUNT; i++)
                workers[i] = new InnerWorker();
        }

        private InnerWorker[] workers;

        public void AddWorker(IWorker worker)
        {
            if (worker != null)
            {
                lock (_sync)
                {
                    if (worker != null)
                    {
                        try
                        {
                            var index = (int)System.Threading.Interlocked.Increment(ref mIndex) % workers.Length;
                            if (mIndex < 0) mIndex = 0;
                            if (index < 0) index = 0;
                            workers[index].Add(worker);
                        }
                        catch
                        {
                            AddWorker(worker);
                        }
                    }
                }
            }
        }

        private class InnerWorker
        {
            private ConcurrentQueue<IWorker> queue = new ConcurrentQueue<IWorker>();
            private volatile bool isStarted = false;
            private const int MAXWAITTIME = 100;
            private int waittime = 0;
            public void Add(IWorker worker)
            {
                if (worker == null) return;
                if (!isStarted)
                {
                    isStarted = true;
                    //new Thread(Execute).Start();
                    System.Threading.ThreadPool.QueueUserWorkItem(Execute);
                }
                queue.Enqueue(worker);
            }
            private IWorker Pop()
            {
                IWorker worker;
                if (queue.TryDequeue(out worker)) return worker;
                return null;
            }
            private void Execute(object state = null)
            {
                while (true)
                {
                    IWorker worker = Pop();
                    if (worker != null)
                    {
                        waittime = 0;
                        worker.Work();
                    }
                    else
                    {
                        System.Threading.Interlocked.Increment(ref waittime);
                        if (waittime > MAXWAITTIME)
                        {
                            isStarted = false;
                            break;
                        }
                    }
                }
            }
        }
    }
}
